/*
 * Copyright (c) 2021 Huawei Device Co.,  Ltd.
 * Licensed under the Apache License,  Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,  software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "cashdevice_napi.h"
#include <string.h>
#include <napi/native_common.h>
#include <napi/native_api.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include "cashdevice_def.h"
#include "cashdevice_client.h"
#include "hilog_wrapper.h"
#include "common_utils.h"

using namespace SwanLink::Finance;

thread_local static CashDeviceClient* g_cashdeviceClient;

namespace SwanLink {
namespace Finance {
namespace Common {
/**
 * @brief 定义接口函数
 * @param env 
 * @param info 
 * @return napi_value 
 */
napi_value GetSupportAccess(napi_env env,  napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 0;
    napi_value args[1] = {0};
    struct tTmpStruct{
        std::vector<DevAccess> ret;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env,  napi_get_cb_info(env,  info,  &argc,  args,  &this_arg,  &priv_data));

    async_excute_work work_func = [](napi_env env,  size_t argc,  napi_value args[],  void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        std::vector<DevAccess> &ret = pVar->ret;

        ret = g_cashdeviceClient->GetSupportAccess();
        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env,  void *data)->napi_value {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result = nullptr;

        result = CppToJsArray(env,  pVar->ret,  [](napi_env env, const void *data)->napi_value {
            const DevAccess* da = static_cast<const DevAccess*>(data);
            napi_value res;

            NAPI_CALL(env,  napi_create_object(env,  &res));
            NAPI_OBJECT_PROP_SET_WITH_LEN(res,  string_utf8,  "name",  NAPI_AUTO_LENGTH,  da->name);
            NAPI_OBJECT_PROP_SET_WITH_LEN(res,  string_utf8,  "desc",  NAPI_AUTO_LENGTH,  da->desc);
            napi_value sub;
            NAPI_OBJECT_OBJ_SET(res,  "transport",  sub);
            NAPI_OBJECT_PROP_SET(sub,  int32,  "mode",  da->transport.mode);
            NAPI_OBJECT_PROP_SET_WITH_LEN(sub, string_utf8, "dev", NAPI_AUTO_LENGTH, da->transport.dev);
            NAPI_OBJECT_PROP_SET(sub, int32, "timeout", da->transport.timeout);

            return res;
        });
        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}

napi_value Open(napi_env env,  napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 2;
    napi_value args[2] = {0};
    struct tTmpStruct{
        char name[MAX_DEV_NAME_LEN];
        Transport trans;
        int ret;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env,  argc >= 2,  "Wrong number of arguments");

    size_t name_len, dev_len;
    NAPI_CALL(env, napi_get_value_string_utf8(env, args[0], pVar->name,
        static_cast<size_t>(MAX_DEV_NAME_LEN), &name_len));
    NAPI_OBJECT_PROP_GET(args[1], int32, "mode", (int32_t*)&pVar->trans.mode);
    NAPI_OBJECT_PROP_GET_WITH_LEN(args[1], string_utf8, "dev", dev_len, pVar->trans.dev);
    NAPI_OBJECT_PROP_GET(args[1], int32, "timeout", (int32_t*)&pVar->trans.timeout);

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        char *name = pVar->name;
        Transport &trans = pVar->trans;
        int &ret = pVar->ret;

        ret = g_cashdeviceClient->Open(name, trans);
        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));

        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}

napi_value Close(napi_env env,  napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = {0};
    struct tTmpStruct{
        int handle;
        int ret;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env,  argc >= 1,  "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        int &handle = pVar->handle;
        int &ret = pVar->ret;

        ret = g_cashdeviceClient->Close(handle);
        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));

        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}

napi_value QueryStatus(napi_env env,  napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = {0};
    struct tTmpStruct{
        int handle;
        int module;
        char err[256];
        int ret;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env,  argc >= 1,  "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        int &handle = pVar->handle;
        int &module = pVar->handle;
        char* err = pVar->err;
        int &ret = pVar->ret;

        ret = g_cashdeviceClient->QueryStatus(handle, module, err);
        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_object(env, &result));
        NAPI_OBJECT_PROP_SET(result,  int32,  "code",  pVar->ret);
        NAPI_OBJECT_PROP_SET_WITH_LEN(result, string_utf8, "err", NAPI_AUTO_LENGTH, pVar->err);
        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}
napi_value Reset(napi_env env,  napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = {0};
    struct tTmpStruct{
        int handle;
        int ret;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env,  argc >= 1,  "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        int &handle = pVar->handle;
        int &ret = pVar->ret;

        ret = g_cashdeviceClient->Reset(handle);
        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));

        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct*>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}


/**
 * @brief 定义枚举类型
 */
napi_value EnumClassConstructor(napi_env env,  napi_callback_info info)
{
    napi_value thisArg = nullptr;
    void* data = nullptr;

    napi_get_cb_info(env,  info,  nullptr,  nullptr,  &thisArg,  &data);

    napi_value global = nullptr;
    napi_get_global(env,  &global);

    return thisArg;
}

napi_value CreateEnumACCESS_MODE(napi_env env, napi_value exports)
{
    napi_value values[2] = {0};
    napi_create_int32(env,  (int32_t)AM_USB,  &values[0]);
    napi_create_int32(env,  (int32_t)AM_SERIAL,  &values[1]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("AM_USB",  values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("AM_SERIAL",  values[1]),
    };
    napi_value result = nullptr;
    napi_define_class(env,  "ACCESS_MODE",  NAPI_AUTO_LENGTH,  EnumClassConstructor,  nullptr, 2,  desc,  &result);
    napi_set_named_property(env,  exports,  "ACCESS_MODE",  result);
    return exports;
}

napi_value DefineStatusCode(const napi_env& env, const napi_value &exports)
{
    napi_property_descriptor desc[] = {
    DECLARE_CONST_VAR(SUCCESS),
    DECLARE_CONST_VAR(ERROR),
    DECLARE_CONST_VAR(CANCEL),
    DECLARE_CONST_VAR(TIMEOUT),
    DECLARE_CONST_VAR(HANDLE_INVALID),
    DECLARE_CONST_VAR(NOT_INIT),

    DECLARE_CONST_VAR(ERR_DEVICE_NOT_OPEN),
    DECLARE_CONST_VAR(ERR_DEVICE_NOT_SUPPORT),
    DECLARE_CONST_VAR(ERR_DEVICE_OPEN_FAILED),
    DECLARE_CONST_VAR(ERR_DEVICE_PERMISSION_DENY),
    DECLARE_CONST_VAR(ERR_DEVICE_NO_DEVBUS),
    DECLARE_CONST_VAR(ERR_DEVICE_DRIVER_ERROR),

    DECLARE_CONST_VAR(ERR_DEVICE_OPEN_ALREADY),
    DECLARE_CONST_VAR(ERR_DEVICE_BUSY),
    DECLARE_CONST_VAR(ERR_DEVICE_NOT_EXIST),
    DECLARE_CONST_VAR(ERR_PLUGIN_UNLOADED),
    DECLARE_CONST_VAR(ERR_PLUGIN_NOT_EXIST),
    DECLARE_CONST_VAR(ERR_PLUGIN_LOAD_FAILED),
    DECLARE_CONST_VAR(ERR_PLUGIN_PATH_UNFIND),
    DECLARE_CONST_VAR(ERR_TRANS_NOT_SUPPORT),
    DECLARE_CONST_VAR(ERR_TRANS_PORT_TACKUP),
    DECLARE_CONST_VAR(ERR_TRANS_PARAM_ERROR),
    DECLARE_CONST_VAR(ERR_TRANS_PROC_ERROR),
    DECLARE_CONST_VAR(ERR_TRANS_PROTO_ERROR),

    DECLARE_CONST_VAR(ERR_TRANS_SEND_FAILED),
    DECLARE_CONST_VAR(ERR_TRANS_SEND_UNFINISHED),
    DECLARE_CONST_VAR(ERR_TRANS_RECV_FAILED),
    DECLARE_CONST_VAR(ERR_TRANS_RECV_UNFINISHED),

    DECLARE_CONST_VAR(ERR_NAPI_PARAM_TYPE_ERROR),
    DECLARE_CONST_VAR(ERR_NAPI_PARAM_NUMBER_WRONG),
    DECLARE_CONST_VAR(ERR_NAPI_PARAM_INVALID),
    DECLARE_CONST_VAR(ERR_CONFIG_LOAD_FAILED),
    DECLARE_CONST_VAR(ERR_CONFIG_NOT_FIND),
    };

    napi_value result = nullptr;
    napi_define_class(env,  "StatusCode",  NAPI_AUTO_LENGTH,  EnumClassConstructor,
        nullptr, sizeof(desc)/sizeof(desc[0]), desc,  &result);
    napi_set_named_property(env,  exports,  "StatusCode",  result);
    return exports;
}

/*
 * function for module exports
 */
static napi_value NapiInit(napi_env env,  napi_value exports)
{
    COMMON_HILOGD(COMMON_JS_NAPI,  "Enter");
    DefineStatusCode(env, exports);
    CreateEnumACCESS_MODE(env,  exports);

    COMMON_HILOGD(COMMON_JS_NAPI,  "Success");

    return exports;
}

napi_value ClientInit(CashDeviceClient *client, napi_env env,  napi_value exports)
{
    g_cashdeviceClient = client;
    return SwanLink::Finance::Common::NapiInit(env, exports);
}
}  // namespace Common
}  // namespace Finance
}  // namespace SwanLink



